package models

import (
	"fmt"
	"go_pure_admin_api/dto"
	"go_pure_admin_api/global"
	"go_pure_admin_api/utils"
	"gorm.io/gorm"
)

// Roles 角色表
type Roles struct {
	gorm.Model
	Name        string  `gorm:"comment:角色名称;unique;size:50;column:name" json:"name" validate:"required" form:"name"`
	Key         string  `gorm:"comment:角色标识;unique;size:50;column:key" json:"key" validate:"required" form:"key"`
	Status      int8    `gorm:"comment:状态 0:禁用 1:启用;default:1" json:"status" form:"status"`
	Remark      string  `gorm:"comment:备注;size:500" json:"remark" form:"remark"`
	Users       []Users `gorm:"many2many:users_roles;" json:"users"`
	Menus       []Menus `gorm:"many2many:roles_menus;" json:"menus"`
	CreatedUser Users   `gorm:"foreignKey:CreatorID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL" json:"created_user"`
	CreatorID   uint    `json:"Creator_id"` // 创建人ID //这里有个大坑千万别和 Users 表的 CreatedUserID 重名，否则会导致外键冲突
}

func init() {
	err := global.DB.AutoMigrate(&Roles{})
	if err != nil {
		global.Logger.Errorf("自动迁移角色表发生错误: %v", err)
		return
	}
}
func (r *Roles) BeforeDelete(*gorm.DB) error {
	if r.Name == "超级管理员" {
		return fmt.Errorf("不能对超级管理员进行删除操作")
	}
	return nil
}
func (r *Roles) GetRolesList(data dto.GetRoleListDto) (roles []Roles, count int64, err error) {
	qs := global.DB.Model(&Roles{})
	// 获取所有角色列表
	if data.Name != "" {
		qs = qs.Where("name LIKE ?", "%"+data.Name+"%")
	}
	if data.Key != "" {
		qs = qs.Where("`key` LIKE ?", "%"+data.Key+"%") // 注意这里的 key 是 MySQL 的保留字，所以需要使用反引号包裹
	}
	if data.Status != "" {
		qs = qs.Where("status = ?", data.Status)
	}
	qs.Count(&count)
	if err := qs.Preload("CreatedUser").Offset((data.CurrentPage - 1) * data.PageSize).Limit(data.PageSize).Find(&roles).Error; err != nil {
		return nil, 0, err
	}
	return
}
func GetRoleMenu() (menus []Menus, err error) {
	err = global.DB.Model(&Menus{}).Where("type != ?", 2).Order("`rank` asc").Find(&menus).Error
	if err != nil {
		return nil, err
	}
	return menus, nil
}

// GetRoleMenuIds 根据角色ID只获取菜单叶子节点的ID
func (r *Roles) GetRoleMenuIds(data dto.GetRoleMenuIdsDto) ([]uint, error) {
	var menuIds []uint
	// 使用CTE（公共表表达式）会更清晰，但GORM不直接支持，所以用Raw SQL
	query := `
		SELECT m.id 
		FROM menus m
		INNER JOIN roles_menus rm ON m.id = rm.menus_id
		WHERE rm.roles_id = ? 
		  AND m.type != 2
		  AND m.deleted_at IS NULL
		  AND m.id NOT IN (
		      SELECT DISTINCT child.parent_id
		      FROM menus child
		      INNER JOIN roles_menus rm2 ON child.id = rm2.menus_id
		      WHERE rm2.roles_id = ?
		        AND child.parent_id IS NOT NULL
		        AND child.deleted_at IS NULL
		        AND child.type != 2
		  )
	`
	err := global.DB.Raw(query, data.ID, data.ID).Pluck("id", &menuIds).Error
	return menuIds, err
}
func (r *Roles) UpdateRole(data dto.UpdateRoleDto) (err error) {
	if err = global.DB.Model(r).Where("id = ?", data.ID).Updates(map[string]interface{}{
		"name":   data.Name,
		"key":    data.Key,
		"status": data.Status,
		"remark": data.Remark,
	}).Error; err != nil {
		return err
	}
	return nil
}

func (r *Roles) CreateRole(data dto.CreateRoleDto) error {
	r.Name = data.Name
	r.Key = data.Key
	r.Status = data.Status
	r.Remark = data.Remark
	if err := global.DB.Create(r).Error; err != nil {
		return err
	}
	return nil
}

func (r *Roles) UpdateRoleMenu(data dto.UpdateRoleMenuDto) error {
	// 1. 参数验证
	if data.ID == 0 {
		return fmt.Errorf("角色ID不能为空")
	}

	// 2. 获取菜单和API数据
	menus, err := GetMenusById(data.MenuIds)
	if err != nil {
		return fmt.Errorf("未能获取菜单信息: %w", err)
	}

	APIs, err := GetAPIsById(data.MenuIds)
	if err != nil {
		return fmt.Errorf("未能获取APIs: %w", err)
	}

	// 3. 查询角色信息
	r.ID = data.ID
	if err := global.DB.First(r, data.ID).Error; err != nil {
		return fmt.Errorf("未能找到角色: %w", err)
	}

	// 4. 使用事务确保数据一致性
	return global.DB.Transaction(func(tx *gorm.DB) error {
		// 4.1 更新菜单关联
		if err := tx.Model(r).Association("Menus").Replace(menus); err != nil {
			return fmt.Errorf("未能更新角色菜单: %w", err)
		}

		// 4.2 更新 Casbin 权限策略
		if err := r.updateCasbinPolicies(APIs); err != nil {
			return fmt.Errorf("未能更新casbin策略: %w", err)
		}

		return nil
	})
}

// updateCasbinPolicies 提取 Casbin 权限更新逻辑
func (r *Roles) updateCasbinPolicies(APIs []Menus) error {
	// 1. 检查是否存在旧策略并删除
	if utils.GetPermissionForRole(r.Key) {
		removed, err := utils.RemovePermissionForRole(r.Key)
		if err != nil {
			return fmt.Errorf("未能移除该角色的原有权限 %s: %w", r.Key, err)
		}
		if !removed {
			return fmt.Errorf("未能移除该角色的原有权限 %s", r.Key)
		}
	}

	// 2. 如果没有新的API权限，直接返回（权限已被清空）
	if len(APIs) == 0 {
		// 可以添加日志记录
		// log.Info("No APIs found for role, permissions cleared", "roleKey", r.Key)
		return nil
	}

	// 3. 添加新的权限策略
	policies := ApiToPolicies(r.Key, APIs)
	if len(policies) == 0 {
		return nil
	}

	added, err := utils.AddPermissionForRole(policies)
	if err != nil {
		return fmt.Errorf("未能为角色添加新权限 %s: %w", r.Key, err)
	}
	if !added {
		return fmt.Errorf("未能为角色添加新权限 %s", r.Key)
	}

	return nil
}

// ApiToPolicies api转policies-权限
func ApiToPolicies(roleKey string, api []Menus) (policies [][]string) {
	for _, v := range api {
		policies = append(policies, []string{roleKey, v.Path, v.Method})
	}
	return
}

func (r *Roles) DeleteRole(data dto.DeleteRoleDto) error {
	if err := global.DB.First(r, data.ID).Error; err != nil {
		return err
	}
	if err := global.DB.Transaction(func(tx *gorm.DB) error {
		// 删除角色与菜单关联
		if err := tx.Model(r).Association("Menus").Clear(); err != nil {
			return err
		}
		// 删除角色与用户的关联
		if err := tx.Model(r).Association("Users").Clear(); err != nil {
			return err
		}
		// 删除角色
		if err := tx.Delete(r).Error; err != nil {
			return err
		}
		// 删除角色权限
		// 判断是否有策略在数据库，如果有在删除。
		if utils.GetPermissionForRole(r.Key) {
			role, err := utils.RemovePermissionForRole(r.Key)
			if err != nil {
				return err
			}
			if !role {
				return fmt.Errorf("删除权限失败")
			}
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

func GetAllRoleList() (roles []Roles, err error) {
	if err := global.DB.Model(&Roles{}).Find(&roles, "status = ?", 1).Error; err != nil {
		return nil, err
	}
	return roles, nil
}
